#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif // _DEFAULT_SOURCE

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ether.h>
#include <time.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "sub_hdr.h"

static int start_flag = 0;
static uint32_t tot_loss = 0;
static uint32_t tot_count = 0;

#define ETHTYPE_OFFSET 12
#define DOT1Q_ETHTYPE_OFFSET 16
#define IP_ID_OFFSET 18
#define DST_IP_OFFSET 30
#define DST_PORT_OFFSET 0x24
#define PAYLOAD_OFFSET 42
#define IPV4_PACKET 0x0008
#define DOT1Q_FRAME 0x0081

#define DST_IP_FILTER  "239.239.240.130"

// 预定义的10的幂数组，支持 decimal 0-9
static const int power_of_10[] = {
    1,          // 10^0 = 1
    10,         // 10^1 = 10
    100,        // 10^2 = 100
    1000,       // 10^3 = 1000
    10000,      // 10^4 = 10000
    100000,     // 10^5 = 100000
    1000000,    // 10^6 = 1000000
    10000000,   // 10^7 = 10000000
    100000000,  // 10^8 = 100000000
    1000000000  // 10^9 = 1000000000
};

extern char *optarg;

static unsigned long ip_to_ulong(const char *ip)
{
    struct in_addr addr;
    if (inet_aton(ip, &addr) == 0)
    {
        fprintf(stderr, "Invalid IP address\n");
        return 0;
    }
    return ntohl(addr.s_addr); // 将网络字节序转换为主机字节序
}

static void proc_packet(const char *buf, int len)
{

    HST_DEPTH_WRAP_S *last_depth = (HST_DEPTH_WRAP_S *)buf;
    HST_BEST_WRAP_S *last_best = (HST_BEST_WRAP_S *)buf;

    time_t rawtime;
    struct tm ts;
    char update[25] = {0};
    char update_nano[80] = {0};

    if (len > 60)
    { // DEPTH
        int factor = (last_depth->decimal < 10) ? power_of_10[last_depth->decimal] : 1;
        
        rawtime = last_depth->tranc_time / 1000000000;
        ts = *localtime(&rawtime);
        strftime(update, sizeof(update), "%Z %Y-%m-%d %H:%M:%S", &ts);
        sprintf(update_nano, "%s.%ld", update, last_depth->tranc_time - (last_depth->tranc_time / 1000000000) * 1000000000);

        printf("<DEEP>%.20s, %u, %s, %d, ", last_depth->symbol, last_depth->rpt_seq, update_nano, last_depth->cum_volumn);
        printf("%.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d | %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d, %.4f_%d\n",
               (double)last_depth->bid_list[9].price_raw / factor, last_depth->bid_list[9].qty,
               (double)last_depth->bid_list[8].price_raw / factor, last_depth->bid_list[8].qty,
               (double)last_depth->bid_list[7].price_raw / factor, last_depth->bid_list[7].qty,
               (double)last_depth->bid_list[6].price_raw / factor, last_depth->bid_list[6].qty,
               (double)last_depth->bid_list[5].price_raw / factor, last_depth->bid_list[5].qty,
               (double)last_depth->bid_list[4].price_raw / factor, last_depth->bid_list[4].qty,
               (double)last_depth->bid_list[3].price_raw / factor, last_depth->bid_list[3].qty,
               (double)last_depth->bid_list[2].price_raw / factor, last_depth->bid_list[2].qty,
               (double)last_depth->bid_list[1].price_raw / factor, last_depth->bid_list[1].qty,
               (double)last_depth->bid_list[0].price_raw / factor, last_depth->bid_list[0].qty,
               (double)last_depth->ask_list[0].price_raw / factor, last_depth->ask_list[0].qty,
               (double)last_depth->ask_list[1].price_raw / factor, last_depth->ask_list[1].qty,
               (double)last_depth->ask_list[2].price_raw / factor, last_depth->ask_list[2].qty,
               (double)last_depth->ask_list[3].price_raw / factor, last_depth->ask_list[3].qty,
               (double)last_depth->ask_list[4].price_raw / factor, last_depth->ask_list[4].qty,
               (double)last_depth->ask_list[5].price_raw / factor, last_depth->ask_list[5].qty,
               (double)last_depth->ask_list[6].price_raw / factor, last_depth->ask_list[6].qty,
               (double)last_depth->ask_list[7].price_raw / factor, last_depth->ask_list[7].qty,
               (double)last_depth->ask_list[8].price_raw / factor, last_depth->ask_list[8].qty,
               (double)last_depth->ask_list[9].price_raw / factor, last_depth->ask_list[9].qty

        );
    }
    else
    { // BEST
        int factor = (last_best->decimal < 10) ? power_of_10[last_best->decimal] : 1;
        
        rawtime = last_best->tranc_time / 1000000000;
        ts = *localtime(&rawtime);
        strftime(update, sizeof(update), "%Z %Y-%m-%d %H:%M:%S", &ts);
        sprintf(update_nano, "%s.%ld", update, last_best->tranc_time - (last_best->tranc_time / 1000000000) * 1000000000);
        printf("<BEST>%.20s, %u, %s, %.4f, %d\n", last_best->symbol, last_best->rpt_seq, update_nano, (double)last_best->price_raw / factor, last_best->last_volumn);
    }
}

int main(int argc, char **argv)
{
    uint16_t* last_ip_id;
    uint16_t pre_ip_id = 0;
    
    int sockfd;
    char buf[10240];
    char in_ifname[64] = {0};
	
    ssize_t n;

    int size = 0;

    struct ifreq req; //网络接口地址
    struct sockaddr_ll addr;

    if (argc < 2)
    {
        printf("Usage: ./demo -i in_ifname\n");
        return 1;
    }

    int opt;
    while ((opt = getopt(argc, argv, "i:")) != -1)
    {
        switch (opt)
        {
        case 'i':
            sprintf(in_ifname, "%s", optarg);
            break;
        default:
            break;
        }
    }

    if (in_ifname[0] == 0)
    {
        printf("if_name is null");
        return 1;
    }

    if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1)
    {
        perror("socket error!");
        return 1;
    }
    strncpy(req.ifr_name, in_ifname, IFNAMSIZ - 1);  //指定网卡名称
    req.ifr_name[IFNAMSIZ - 1] = '\0';  // 确保字符串以null结尾
    if (-1 == ioctl(sockfd, SIOCGIFFLAGS, &req)) //获取网络接口
    {
        perror("ioctl");
        exit(-1);
    }

    req.ifr_flags |= IFF_PROMISC;
    if (-1 == ioctl(sockfd, SIOCGIFFLAGS, &req)) //网卡设置混杂模式
    {
        perror("ioctl");
        exit(-1);
    }

    // 绑定套接字到指定网卡
    memset(&addr, 0, sizeof(addr));
    addr.sll_family = PF_PACKET;
    addr.sll_protocol = htons(ETH_P_ALL);
    addr.sll_ifindex = if_nametoindex(req.ifr_name);
    if (addr.sll_ifindex == 0)
    {
        fprintf(stderr, "Unknown interface: %s\n", req.ifr_name);
        exit(EXIT_FAILURE);
    }
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        n = recv(sockfd, buf, sizeof(buf), 0);

        if (n == -1)
        {
            perror("recv error!");
            break;
        }
        else if (n == 0)
            continue;

        size = n;
        char *packet_buf = (char *)(buf);

        if (size > 42)
        {
            uint16_t *ethtype = (uint16_t *)(packet_buf + ETHTYPE_OFFSET);
            uint16_t *dot1q_ethtype = (uint16_t *)(packet_buf + DOT1Q_ETHTYPE_OFFSET);

            int dot1q_offset = 0;

            if (*ethtype == IPV4_PACKET)
                dot1q_offset = 0;
            else if (*ethtype == DOT1Q_FRAME && *dot1q_ethtype == IPV4_PACKET)
                dot1q_offset = 4;
            else
                continue;

            uint32_t dst_ip = ntohl(*((uint32_t *)(packet_buf + dot1q_offset + DST_IP_OFFSET)));
            if (dst_ip != ip_to_ulong(DST_IP_FILTER)) 
                continue;

            uint16_t dst_port = ntohs(*((uint16_t *)(packet_buf + dot1q_offset + DST_PORT_OFFSET)));
            if (dst_port == 9999)
                continue;

            tot_count++;

            // packet loss detect
            last_ip_id =  (uint16_t *) (packet_buf + IP_ID_OFFSET);
            if (start_flag && *last_ip_id != (uint16_t)(pre_ip_id+1)) {
                uint32_t loss = *last_ip_id > pre_ip_id ? *last_ip_id - pre_ip_id - 1 : 0xffff - pre_ip_id + *last_ip_id - 1;
                tot_loss += loss;
                printf("<GAP>  pre_id: %5d, last_id:%5d, tot_loss %d, tot_count %d\n", pre_ip_id,  *last_ip_id, tot_loss, tot_count);
            } 
            if (*last_ip_id % 5000 == 0)
                printf("<COUNT>  pre_id: %5d, last_id:%5d, tot_loss %d, tot_count %d\n", pre_ip_id,  *last_ip_id, tot_loss, tot_count);

            pre_ip_id = *last_ip_id;
            start_flag = 1;
            proc_packet(packet_buf + PAYLOAD_OFFSET + dot1q_offset, size - PAYLOAD_OFFSET - dot1q_offset);
        }
    }

    close(sockfd);

    return 0;
}
